﻿using System;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace Spacer {
    public enum EnemyAIState {
        Chasing,
        Caught,
        Wander,
        Fleeing
    }

	// ----------

	public class Enemy : Pawn {
        public float ChaseDistance { get; set; }
        public float ChaseHysteresis { get; set; }
        public EnemyAIState CurrentState { get; set; }
        public Ship Target { get; set; }
        public float CatchDistance { get; set; }
        public float FireDistance { get; set; }
        Random random;
        public float MaxVelocity { get; set; }
        public float TurnVelocity { get; set; }
		public float LastFireTime { get; set; }
        public float FireRate { get; set; }

        private GameTime lastFireTime;

		// ----------
		
		public Enemy() {}

        public Enemy(SolarSys world) : base(world) {
			if( this.GetType() == typeof(Enemy) ) Initialize();
		}

		public override void Initialize() {
			base.Initialize();

			DrawMode = DrawMode.Sprite;
			Sprite = Main.Sprites["Fighter"];

			DrawColor = new Color( 1f, 1f, 1f, 1f );
            DrawScale = DrawScale * .3f;
			PhysicsInit( 1f, MovementMode.Normal );
			Friction = 0.3f;
			AngularDamping = 3f;
			LinearDamping = 1f;

            CurrentState = EnemyAIState.Wander;
            ChaseDistance = 40f;
            ChaseHysteresis = 4f;
            CatchDistance = 10f;
            FireDistance = 20f;

            Target = null;
            random = new Random();

            MaxVelocity = 9f;
            TurnVelocity = 7f;
		
			Health = 200f;
			MaxHealth = 200f;
            LastFireTime = Globals.GameTime;
            FireRate = .3f;
		}

        public override void Update()
        {
            UpdateEnemyState();
            PerformAction();

            base.Update();
        }

        private void UpdateEnemyState()
        {
            if (Target == null)
            {
                foreach (Entity ent in World.FindObjects(Position, ChaseDistance - ChaseHysteresis))
                {
                    if (ent is Ship)
                    {
                        if (Target == null || Vector2.Distance(Position, ent.Position) < Vector2.Distance(Position, Target.Position))
                        {
                            Target = (Ship)ent;
                            CurrentState = EnemyAIState.Chasing;
                        }
                    }
                }
            }
            else if (Vector2.Distance(Position, Target.Position) > (ChaseDistance + ChaseHysteresis))
            {
                CurrentState = EnemyAIState.Wander;
                Target = null;
            }
            else if (Vector2.Distance(Position, Target.Position) < CatchDistance)
            {
                CurrentState = EnemyAIState.Caught;
            }
            else if (!Target.IsAlive)
            {
                CurrentState = EnemyAIState.Wander;
                Target = null;
            }
            else if (Health < MaxHealth * .2f)
            {
                CurrentState = EnemyAIState.Fleeing;
            }
            else
            {
                CurrentState = EnemyAIState.Chasing;
            }
        }

        private void PerformAction()
        {
            // Third, once we know what state we're in, act on that state.
            float currentShipSpeed;
            if (CurrentState == EnemyAIState.Chasing)
            {
                // the tank wants to chase the cat, so it will just use the TurnToFace
                // function to turn towards the cat's position. Then, when the tank
                // moves forward, he will chase the cat.
                PhysicsObject.ApplyTorque(TurnToFace(Target.Position));
                currentShipSpeed = MaxVelocity;
                if (Vector2.Distance(Position, Target.Position) < FireDistance)
                {
                    DoFire();
                }
            }
            else if (CurrentState == EnemyAIState.Wander)
            {
                Wander();
                currentShipSpeed = .25f * MaxVelocity;
            }
            /*
            else if (CurrentState == EnemyAIState.Fleeing)
            {
                PhysicsObject.ApplyTorque(TurnToFace(Vector2.Normalize(Target.Position-Position)+Position));
                currentShipSpeed = MaxVelocity;
            }
            */
            else
            {
                currentShipSpeed = -MaxVelocity;
            }

            Vector2 heading = new Vector2((float)Math.Cos(Angle), (float)Math.Sin(Angle));
            PhysicsObject.ApplyForce(heading * currentShipSpeed);
        }

        private void Wander()
        {
            Vector2 wanderDirection = Vector2.Zero;
            wanderDirection.X = Position.X + MathHelper.Lerp(-1f, 1f, (float)random.NextDouble());
            wanderDirection.Y = Position.Y + MathHelper.Lerp(-1f, 1f, (float)random.NextDouble());
            PhysicsObject.ApplyTorque(TurnToFace(wanderDirection));
        }

        private float TurnToFace(Vector2 faceThis)
        {
            //get if target is left or right
            Vector2 Right = new Vector2(
                (float)Math.Cos(Angle + MathHelper.PiOver2),
                (float)Math.Sin(Angle + MathHelper.PiOver2)
            );
            Vector2 TDir = faceThis - Position;
            TDir.Normalize();
            float Dot = Vector2.Dot(Right, TDir);

            //find the angle between
            Vector2 Forward = new Vector2(
                (float)Math.Cos(Angle),
                (float)Math.Sin(Angle)
            );
            Vector2 faceThisNormalize = Position - faceThis;
            faceThisNormalize.Normalize();

            float temp = Vector2.Dot(Forward, faceThisNormalize);

            float angleBetween = (float)Math.Acos(MathHelper.Clamp(temp,-1f,1f));

            float ret = (TurnVelocity * angleBetween - PhysicsObject.AngularVelocity * 15) / Mass;
            if (Dot < 0)
                ret *= -1;

            if (Single.IsNaN(ret))
                ret = ret;

            return ret;
        }

        private static float WrapAngle(float radians)
        {
            while (radians < -MathHelper.Pi)
            {
                radians += MathHelper.TwoPi;
            }
            while (radians > MathHelper.Pi)
            {
                radians -= MathHelper.TwoPi;
            }
            return radians;
        }

        public virtual void DoFire()
        {
            if (Globals.GameTime - LastFireTime >= FireRate)
            {
                FireProjectile(typeof(Projectile), GetShootPos());
                LastFireTime = Globals.GameTime;
            }
        }


        public override Vector2 GetShootPos()
        {
            if (PhysicsObject != null)
            {
                Vector2 offset = Vector2.Transform(PhysicsObject.LocalCenter + new Vector2(0.75f, 0f), Matrix.CreateRotationZ(Angle));
                return Position + offset;
            }

            return Position;
        }
	}
}
